;; supercollider/synthdef.scm - (c) rohan drape, 2003-2005

;; A synthdef declaration is a set of three values.  A string `name',
;; a possibly empty list `controls', and a sequence of s-expressions.
;; Requires the 'with-control-set' syntax.  This syntax returns the
;; graphdef encoding of a synthdef declaration.

(define-syntax synthdef
  (syntax-rules ()
    ((_ name (control ...) expr)
     (ugen-graph->graphdef name (with-control-set (control ...) expr)))))

;; The directory that synthdefs will be written to unless the user
;; goes to some trouble to do otherwise.  Synthdef files are
;; considered object files, not user managed files.

(define-global synthdef-directory "/tmp")

;; Construct the file-name of the file to write the synthedef with
;; `name' to.  The RSC convention is that a synthdef file contains a
;; single graphdef, and that the name of graphdef is used to name the
;; synthdef file, with the addition of the '.scsyndef' extension.

(define (synthdef-file-name name)
  (format "~a/~a.scsyndef" (synthdef-directory) name))

;; Write the graphdef file associated with the synthdef `s'.

(define (synthdef-write s)
  (let ((g (as-graphdef s)))
    (graphdef-write-file g (synthdef-file-name (graphdef-name g)))))

;; Load the synthdef file describing the synthdef `name' at `s'.

(defineK (synthdef-load (name "default") (synchronous #t))
  (server-request* synchronous
		   "/done"
		   (list "/d_load" (synthdef-file-name name))))

;; Composition of 'synthdef-write' and 'synthdef-load'.  The load is
;; only initiated if `server' is booted.

(defineK (synthdef-store (s #f) (synchronous #t))
  (synthdef-write s)
  (if (server-booted?)
      (synthdef-load (graphdef-name (as-graphdef s)) synchronous)))

;; Send the bytecode describing the synthdef `s' to `server'.  If
;; `synchronous' is true the send operation is synchronous.

(defineK (synthdef-send (s #f) (synchronous #t))
  (server-request* synchronous
		   "/done"
		   (list "/d_recv" (graphdef-encode (as-graphdef s)))))

;; The target to use when playing a synthdef declaration.

(define-parameter synthdef-play-target 1)

;; Evaluates to a synth record that is playing the synthdef `s' at the
;; target synthdef-play-target.

(define (synthdef-play s)
  (let ((g (as-graphdef s)))
    (synthdef-send g #t)
    (-> "/s_new"  (graphdef-name g) -1 1 (synthdef-play-target))))

;; Transform a <ugen> into a <graphdef>.  If ugen is not an instance
;; of Out encapsulate it.

(define (ugen->synthdef/out ugen)
  (if (or (list? ugen)
	  (not (string=? (ugen-name ugen) "Out")))
      (synthdef "Anonymous" ((bus 0)) (Out.ar bus ugen))
      (synthdef "Anonymous" () ugen)))

;; Procedure to play an expression.  

(define (play value)
  (synthdef-play 
   (if (or (ugen? value) (uniform? (or? ugen? output-proxy?) value))
       (ugen->synthdef/out value)
       value)))

